package pl.edu.agh.neural.input;

import pl.edu.agh.bp.BpLayer;
import pl.edu.agh.neural.core.*;
import pl.edu.agh.neural.simple.InputConnection;
import pl.edu.agh.neural.simple.SimpleNeuron;
import pl.edu.agh.neural.simple.activation.IActivationFunction;
import pl.edu.agh.neural.simple.activation.LinearActivation;
import pl.edu.agh.neural.simple.activation.LogisticActivation;
import pl.edu.agh.neural.simple.activation.StepActivation;
import pl.edu.agh.som.learning.ILearningFunction;
import pl.edu.agh.som.learning.LinearLearning;

import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class BpNetworkBuilder {

    public static ITrainableNetworkWithTeacher BuildFromFile(String filepath, IWeightsGenerator weightsGenerator) throws Exception {
        List<String> lines = Files.readAllLines(Paths.get(filepath), Charset.forName("UTF-8"));

        String line = lines.get(0).trim();

        String[] parts =  line.split("\\s+");
        int neuronsCount = Integer.parseInt(parts[0]);
        boolean hasBias = parts.length == 2 && parts[1].equals("bias");

        List<InputNeuron> inputNeurons = new ArrayList<>(neuronsCount);
        for (int i = 0; i < neuronsCount; i++)
        {
            inputNeurons.add(new InputNeuron());
        }

        InputLayer inputLayer = new InputLayer(inputNeurons, hasBias);

        List<ITrainableLayerWithTeacher> layers = new ArrayList<>();

        ILayer previousLayer = inputLayer;

        for (int i = 1; i < lines.size(); i++) {
            line = lines.get(i).trim();
            parts =  line.split("\\s+");
            IActivationFunction activationFunction = getActivationFunction(parts[0], Double.parseDouble(parts[1]));
            neuronsCount = Integer.parseInt(parts[2]);
            hasBias = parts.length == 4 && parts[3].equals("bias");
            List<? extends INeuron> previousLayerNeurons = previousLayer.getNeurons();

            List<SimpleNeuron> neurons = new ArrayList<>(neuronsCount);
            for (int j = 0; j < neuronsCount; j++)
            {
                InputConnection[] inputConnections = new InputConnection[previousLayerNeurons.size()];
                for (int k = 0; k < previousLayerNeurons.size(); k++) {
                    double weight = weightsGenerator.nextWeight();
                    INeuron connectedNeuron = previousLayerNeurons.get(k);
                    inputConnections[k] = new InputConnection(connectedNeuron, weight);
                }
                neurons.add(new SimpleNeuron(activationFunction, inputConnections));
            }

            ITrainableLayerWithTeacher layer = new BpLayer(neurons, hasBias);
            layers.add(layer);
            previousLayer = layer;
        }

        return new BasicTrainableNetworkWithTeacher(inputLayer, layers);
    }

    private static IActivationFunction getActivationFunction(String funName, double activationParameter)
            throws FileParsingException {
        if (funName.startsWith("lin"))
            return new LinearActivation(activationParameter);
        if (funName.startsWith("ste"))
            return new StepActivation(activationParameter);
        if (funName.startsWith("sig"))
            return new LogisticActivation(activationParameter);
        throw new FileParsingException("Unknown activation function: " + funName);
    }
}
